//
//  LZVideoResourceLoader.m
//  laziz_Consumer
//
//  Created by xbnzd on 17/3/29.
//  Copyright © 2017年 XBN. All rights reserved.
//

#import "LZVideoResourceLoader.h"
#import "LZVideoCacheManager.h"
#import "LZRequestVideoTask.h"

#import <MobileCoreServices/MobileCoreServices.h>

@interface LZVideoResourceLoader ()<LZRequestVideoTaskDelegate>
@property (nonatomic, strong) NSMutableArray *requestArr;  //存储所有发起的请求.
@property (nonatomic, strong) NSString *videoLocalTempPath;   //视频本地存储路径
@property (nonatomic, strong) LZRequestVideoTask *task;
@end

@implementation LZVideoResourceLoader

- (instancetype)init
{
    self = [super init];
    if (self) {
        _requestArr = [[NSMutableArray alloc] init];
    }
    
    return self;
}


#pragma mark - 配置数据,或者操作下载等相关方法

/**
 配置请求,是从0开始,还是已经下载了一部分.需要继续下载,或者跳跃下载

 @param resourceRequest 请求
 */
- (void)configureLoadingRequest:(AVAssetResourceLoadingRequest *)resourceRequest
{
    //获取请求url
    NSURL *requestURL = [resourceRequest.request URL];
    //resourceRequest.dataRequest 这次请求当前最新的数据量
    NSRange range = NSMakeRange((NSUInteger)resourceRequest.dataRequest.currentOffset, NSUIntegerMax);
    
    if (self.task.currentDownLoadingOffset > 0) {
        [self operationHaveDownloadedRequest];
    }
    
    if (self.task) {//已经有开始下载
        /*************************************
         这里只判断两种情况. 举例说明 : 视频全长 0 -- 100;
         1,视频已经播放到 20, 向后拖拽到了60,后续请求从60开始请求,(下面方法做了个容量处理,就是向后拖拽比当前缓存只多300K的.话.默认不处理)
         2,视频从60播放到了70,又向前拖拽到了40, 因为之前 20到60这段未缓存, 这时,后续请求从40开始
         注:正常情况下.如果已经缓存到80或者已经缓存完毕,但是播放到20.这时向后拖拽到40,经测试,视频是不会走resourceload的协议的.
         ************************************/
        if (self.task.wantDownLoadOffset + self.task.currentDownLoadingOffset + 1024*300 < range.location || range.location < self.task.wantDownLoadOffset) {
            
            [self.task loadVideoWithURL:requestURL offset:range.location];
        }
    }else{//没有下载过
        
        self.task = [[LZRequestVideoTask alloc] init];
        self.task.lzRequestVideoTaskDelegate = self;
        [self.task loadVideoWithURL:requestURL offset:0];
    }
}


/**
 处理已经下载的请求,(分片请求,其他片已经下载), 看是否已经下载完成.下载完成了.则把这个数据填充给播放器.然后删除请求
 */
- (void)operationHaveDownloadedRequest
{
    [self.requestArr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        AVAssetResourceLoadingRequest *req = (AVAssetResourceLoadingRequest *)obj;
        [self fillInContentInformation:req.contentInformationRequest];
        
        //判断该片是否下载完成
        BOOL didResponseCompleted = [self checkTheRequestHasCompleted:req.dataRequest];
        
        if (didResponseCompleted) {
            //发布消息,该片段下载完成
            [req finishLoading];
            *stop = YES;
            if (*stop == YES) {
                [self.requestArr removeObject:req];
            }
        }

    }];

}


/**
 检查当前的request 是否已经请求完成

 @param dataRequest <#resourceRequest description#>
 @return <#return value description#>
 */
- (BOOL)checkTheRequestHasCompleted:(AVAssetResourceLoadingDataRequest *)dataRequest
{
#warning 这里的判断依据应该是当前下载的内容比这段内容大就可以. 但是没搞明白dataRequest.currentOffset dataRequest.requestLength 这个两个参数的真实意义.经测试打印,这两个值是会变的. currentOffset 会变小 需要再研究.
    /***************************
    dataRequest 是当前分片下载的请求. 比如整个数据是 0--100.
    dataRequest.requestedOffset 如果没有拖拽,这个值始终是0 ,如果拖拽了.这个值会更新.比如拖拽后.从20开始播放,这个值就20
    同理.dataRequest.currentOffset的值就是从requestedoffset 开始下载的值. 如果没有拖拽,上面的是0. 这个值会一直更新:1 2 3....如果上面的值拖拽到20开始下载.那这个值的返回从20开始: 20 21 22....等
    dataRequest.requestLength. 这个值如果没有拖拽.则是整个文件的长度 100. 如果上面拖拽到了20开始下载.那这个值就是 100 - 20 = 80
    ******************************/
    
    //获取该asset的第一个字节位置
    long long startOffset = dataRequest.requestedOffset;
    //dataRequest.currentOffset 当前asset正在下载的当前字节,有值,说明拿到了部分数据
    if (dataRequest.currentOffset != 0) {
        startOffset = dataRequest.currentOffset;
    }
    
    //如果task想要请求开始的量 + task正在请求的量, 小于asset的量,说明该片请求没有完成==有可能是正长下载未完成.或者是向后拖拽了播放进度
    if ((self.task.wantDownLoadOffset + self.task.currentDownLoadingOffset) < startOffset) {
        return NO;
    }
    
    //如果小于已经下载的量.说明也是未完成.此处应是针对向前拖拽了播放进度
    if (startOffset < self.task.wantDownLoadOffset) {
        return NO;
    }
    
    //本地已经存储的数据
    NSData *localData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:_videoLocalTempPath] options:NSDataReadingMappedIfSafe error:nil];
    if (localData.length == 0) {
        return NO; 
    }

    NSUInteger unreadBytes = self.task.currentDownLoadingOffset - ((NSInteger)startOffset - self.task.wantDownLoadOffset);
    
    NSUInteger numberOfBytesToRespondWith = MIN((NSUInteger)dataRequest.requestedLength, unreadBytes);
    
    [dataRequest respondWithData:[localData subdataWithRange:NSMakeRange((NSUInteger)startOffset- self.task.wantDownLoadOffset, (NSUInteger)numberOfBytesToRespondWith)]];

    long long endOffset = startOffset + dataRequest.requestedLength;

    BOOL didRespondFully = (self.task.wantDownLoadOffset + self.task.currentDownLoadingOffset) >= endOffset;

   
    return didRespondFully;
}

/**
 对每次请求加上长度, 文件类型等信息

 @param contentInformationRequest <#contentInformationRequest description#>
 */
- (void)fillInContentInformation:(AVAssetResourceLoadingContentInformationRequest *)contentInformationRequest
{
    NSString *mimeType = self.task.mimeType;
    CFStringRef contentType = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)(mimeType), NULL);
    contentInformationRequest.byteRangeAccessSupported = YES;
    contentInformationRequest.contentType = CFBridgingRelease(contentType);
    contentInformationRequest.contentLength = self.task.videoLength;
}

#pragma mark - 更改scheme. 并读取临时数据的位置
/**
 曲线设置.更改scheme后,系统找不到正确的scheme, 才会走本类的代理方法.问为啥.请自行bai~du

 @param vUrl <#vUrl description#>
 @return <#return value description#>
 */
- (NSURL *)getSchemeVideoURL:(NSURL *)vUrl
{
    NSURLComponents *c = [[NSURLComponents alloc] initWithURL:vUrl resolvingAgainstBaseURL:NO];
    c.scheme = @"lazizccc";
    
    _videoLocalTempPath = [[LZVideoCacheManager shareManager] getCacheTemporaryPath];
    
    return [c URL];
}


#pragma mark - 系统 resourceloader delegate

/**
 视频器加载在线视频,发现scheme不对后,会调用这个代理方法.我们需要在这个代理方法中去请求数据源,会多次调用

 @param resourceLoader <#resourceLoader description#>
 @param loadingRequest <#loadingRequest description#>
 @return <#return value description#>
 */
- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
    //把每一次的请求放入数组
    [self.requestArr addObject:loadingRequest];
    
    [self configureLoadingRequest:loadingRequest];
    
    return YES;
}

- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest
{
    [self.requestArr removeObject:loadingRequest];
    
    DLog(@"**取消下载或者下载完毕");
}

#pragma mark - task  delegate
- (void)taskDidReciveVideoLength:(NSUInteger)videoLength mimeType:(NSString *)mimeType task:(LZRequestVideoTask *)task
{}

- (void)taskDidReciveVideoDataWithTask:(LZRequestVideoTask *)task
{
    //接受到了数据,就开始处理数据
    [self operationHaveDownloadedRequest];
}

- (void)taskDidFinishLoadingWithTask:(LZRequestVideoTask *)task
{}

- (void)taskDidFailLoadingWithTask:(LZRequestVideoTask *)task withError:(NSInteger)errorCode
{
    if (self.lzResourceLoaderDelegate && [self.lzResourceLoaderDelegate respondsToSelector:@selector(resourceLoaderDidFailLoadingWithTask:withErrorCode:)]) {
        [self.lzResourceLoaderDelegate resourceLoaderDidFailLoadingWithTask:task withErrorCode:errorCode];
    }
}

- (void)taskStarLoadingWithProgress:(CGFloat)progress
{
    if (self.lzResourceLoaderDelegate && [self.lzResourceLoaderDelegate respondsToSelector:@selector(resourceLoaderDidStartLoadingWithPrgoress:)]) {
        [self.lzResourceLoaderDelegate resourceLoaderDidStartLoadingWithPrgoress:progress];
    }
}

@end
